using System;
using System.Collections.Generic;
using System.Net;
using System.IO;
using Microsoft.Ccr.Core;

namespace BitTorrent.Ccr.Adapters.IO
{
    internal static class WebRequestAdapter
    {
        public static PortSet<byte[], Exception> ReadResponse(WebRequest request, DispatcherQueue dispatcherQueue) {
            return ReadResponse(request, new PortSet<byte[], Exception>(), dispatcherQueue);
        }

        public static PortSet<byte[], Exception> ReadResponse(WebRequest request, PortSet<byte[], Exception> resultPort, DispatcherQueue dispatcherQueue) {
            dispatcherQueue.Enqueue(new IterativeTask(() => DoReadResponse(request, resultPort)));
            return resultPort;
        }

        private static IEnumerator<ITask> DoReadResponse(WebRequest request, PortSet<byte[], Exception> resultPort) {
            Port<IAsyncResult> responsePort = new Port<IAsyncResult>();

            try {
                request.BeginGetResponse(responsePort.Post, null);
            } catch (Exception ex) {
                resultPort.Post(ex);
                yield break;
            }

            yield return responsePort.Receive();

            WebResponse response = null;
            try {
                // REVISIT (fen): Have to use Test to get the IAsyncResult is there any other way?
                IAsyncResult r;
                responsePort.Test(out r);
                response = request.EndGetResponse(r);
            } catch (Exception ex) {
                resultPort.Post(ex);
                yield break;
            }


            Stream responseStream = response.GetResponseStream();
            long contentLength = response.ContentLength;
            int totalRead = 0;
            byte[] buffer = new byte[0x10000];
            Port<IAsyncResult> readPort = new Port<IAsyncResult>();
            MemoryStream resultStream = new MemoryStream((int)contentLength);
            while (totalRead < contentLength) {
                try {
                    responseStream.BeginRead(buffer, 0, buffer.Length, readPort.Post, null);
                } catch (Exception ex) {
                    resultPort.Post(ex);
                    yield break;
                }
                yield return readPort.Receive();

                IAsyncResult r;
                readPort.Test(out r);

                int read = responseStream.EndRead(r);
                totalRead += read;
                resultStream.Write(buffer, 0, read);
            }
            resultPort.Post(resultStream.ToArray());

            yield break;
        }
    }
}
